#!/usr/bin/env python

import serial

class HackIR:
	'''
	HackIR class, used for transmitting and receiving IR codes via the HackIR board.
	'''

	PORT = "/dev/ttyACM"
	RECEIVE = 'R'
	TRANSMIT = 'T'
	ACK = "ACK"
	NACK = "NACK"

	def __init__(self, port=None):
		'''
		Class constructor.

		@port - HackIR serial port. Will be auto-detected if not specified.

		Returns None.
		'''
		self._open(port)
	
	def _open(self, port):
		'''
		Opens a connection to the HackIR board. Called by the class constructor.
		For internal use only.
		'''
		last_error = ""

		if port is not None:
			self.port = port
			self.serial = serial.Serial(self.port)
		else:
			# Try to open up the first /dev/ttyACMX port found
			for i in range(0, 10):
				try:
					self.port = self.PORT + str(i)
					self.serial = serial.Serial(self.port)
					break
				except Exception, e:
					last_error = str(e)
					self.port = None

                        if self.port is None:
                                raise Exception(last_error)

	def _readline(self):
		'''
		Reads one line from the HackIR board.
		For internal use only.
		'''
		line = ''

		while not line:
			line = self.serial.readline().strip()

		return line

	def _writeline(self, string):
		'''
		Writes one line to the HackIR board.
		For internal use only.
		'''
		self.serial.write(string + '\n')

	def String2Code(self, string):
		'''
		Converts an IR code string into a code dictionary.

		@string -The IR code string.
	
		Returns a code dictionary.
		'''
		code = {}

		# NEC:0xC12FA15E:32:0:0x00000000
		(code['type'], code['value'], code['bits'], code['repeat'], code['address']) = string.split(':')
	
		code['value'] = int(code['value'], 16)
		code['bits'] = int(code['bits'])
		code['repeat'] = int(code['repeat'])
		code['address'] = int(code['address'], 16)
	
		return code

	def Code2String(self, code):
		'''
		Converts an IR code dictionary into a string.

		@code - A code dictionary.

		Returns a string representing the supplied code dictionary.
		'''
		return "%s:0x%.8X:%d:%d:0x%.8X" % (code['type'], code['value'], code['bits'], code['repeat'], code['address'])

	def Receive(self, count=1):
		'''
		Receives IR codes from the HackIR board.
		
		@count - Number of IR codes to receive. Default: 1.

		Returns a list of IR code dictionaries.
		'''
		codes = []

		for i in range(0, count):
			self.serial.write(self.RECEIVE)
			codes.append(self.String2Code(self._readline()))

		return codes

	def Transmit(self, codes):
		'''
		Transmits IR codes via the HackIR board.

		@codes - A list of IR codes to transmit.

		Returns None.
		'''
		for code in codes:
			self._writeline(self.TRANSMIT + self.Code2String(code))
			if self._readline() != self.ACK:
				raise Exception("Unsupported IR encoding type: %s" % code['type'])

	def Close(self):
		'''
		Closes the connection to the HackIR board.
		
		Returns None.
		'''
		self.serial.close()

if __name__ == '__main__':
	# Simple capture and replay code example
	ir = HackIR()
	print "Connected to", ir.port
	codes = ir.Receive()
	print ir.Code2String(codes[0])
	ir.Transmit(codes)
	ir.Close()
